/* TPE Teoria de Lenguajes y Automatas - GLUCK */

%{
#include <math.h>
#include <stdio.h>

#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include "Tree.h"

int yylex (void);
void yyerror (char const *);
FILE * fd, * input;
extern FILE *yyout, *yyin;
struct nodeT * root;
struct nodeT * aux1;
struct nodeT * aux2;
struct nodeT * aux3;
struct nodeT * aux4;
struct nodeT * aux5;
struct nodeT * aux6;
int auxelse = 0;
int auxelseif = 0;
int auxarrdecl = 0;
int auxadecl = 0;
int auxbody = 0;
int auxifnever = 0;
int auxifquit = 0;
int auxexpr2 = 0;
int auxarray = 0;
int auxparam = 0;
%}

%union
{
	char * cadena;
	int numero;
	float numero2;
	struct nodeT * node;
}

%token ARROW
%token ASSIGN
%token OPEN_BRACE
%token CLOSE_BRACE
%token OPEN_PAREN
%token CLOSE_PAREN
%token IFNEVER
%token IFQUIT
%token <cadena> ID
%token <cadena> T_INT
%token <cadena> T_REAL
%token <cadena> T_BOOLEAN
%token <cadena> T_STRING
%token INT
%token REAL
%token BOOLEAN
%token STRING
%token <cadena> FUNCTION
%token IF
%token ELSEIF
%token ELSE
%token WHILE
%token <cadena> COLON
%token <cadena> SEMICOLON
%token THEN
%token LOOP
%token COMMA

%token TRUE
%token FALSE
%token ENTER
%token TAB
%token QUIT
%token DOT
%token OPEN_BRACKET
%token CLOSE_BRACKET
%token TO
%token NOT
%token LITERAL

%type <node> gluck expr fdecl aux params param_list expr2 op vdecl auxt in_prm out_prm body unary body_params
%type <node> elseif else ifnever ifquit array arrdecl arrdecl2 arr_init arr_exp expr_arr adecl asig

%right ASSIGN
%right EQ NEG MIN MINE GRT GRTE
%left AND OR
%left PLUS MINUS
%left BY DIVIDE
%left MOD

%%
gluck2:		gluck	{root = $<node>1;}
;
gluck:		/* lambda */ {$$ = creaNodo("GLUCK");}
		|	fdecl gluck {$$ = llenaHijos($<node>2,1, $<node>1);}
;

expr:           		unary ID params expr2 {$$ = creaNodo($<cadena>2); if(auxparam) $$ = llenaHijos($<node>$, 1, $<node>3); if($<node>1 != NULL) $$ = llenaHijos($<node>1, 1, $<node>$); if(auxexpr2) $$ = llenaHijosIzq($<node>4, 1, $<node>$); auxparam=0; auxexpr2 = 0;}
                |       unary INT expr2 {$$ = creaNodo($<cadena>2); if($<node>1 != NULL) $$ = llenaHijos($<node>1, 1, $<node>$); if($<node>3 != NULL ) $$ = llenaHijosIzq($<node>3, 1, $<node>$); auxexpr2 = 0;}
                |       unary REAL expr2 {$$ = creaNodo($<cadena>2); if($<node>1 != NULL) $$ = llenaHijos($<node>1, 1, $<node>$); if(auxexpr2) $$ = llenaHijosIzq($<node>3, 1, $<node>$); auxexpr2 = 0;}
                |       unary BOOLEAN expr2 {$$ = creaNodo($<cadena>2); if($<node>1 != NULL) $$ = llenaHijos($<node>1, 1, $<node>$); if(auxexpr2) $$ = llenaHijosIzq($<node>3, 1, $<node>$); auxexpr2 = 0;}
                |       LITERAL expr2 {$$ = creaNodo($<cadena>1); if(auxexpr2) $$ = llenaHijosIzq($<node>2, 1, $<node>$); auxexpr2 = 0;}
                |       OPEN_PAREN expr CLOSE_PAREN expr2 {$$ = creaNodo("EXPR"); aux1 = creaNodo($<cadena>1); aux2 = creaNodo($<cadena>3); llenaHijos($<node>$, 3,aux1, $<node>2, aux2); if ($<node>4 != NULL) $$=llenaHijosIzq($<node>4, 1,$<node>$);}
//                |       NOT expr {aux1 = creaNodo($<cadena>1); $$ = llenaHijos(aux1, 1, $<node>2);}
//                |       MINUS expr {aux1 = creaNodo($<cadena>1); $$ = llenaHijos(aux1, 1, $<node>2);}
//                |       PLUS expr {aux1 = creaNodo($<cadena>1); $$ = llenaHijos(aux1, 1, $<node>2);}
;
params:			/* lambda */ {auxparam = 0; $$ = creaNodo("PARAMS");}
				|		OPEN_PAREN param_list CLOSE_PAREN  {auxparam = 1; $$ = creaNodo("PARAMS"); $$ = llenaHijos($<node>$, 1, $<node>2);}
				|		OPEN_PAREN CLOSE_PAREN	{auxparam = 1; $$ = creaNodo("PARAMS");}
;

body_params:			OPEN_PAREN param_list CLOSE_PAREN  {auxparam = 1; $$ = creaNodo("PARAMS"); $$ = llenaHijos($<node>$, 1, $<node>2);}
				|		OPEN_PAREN CLOSE_PAREN	{auxparam = 1; $$ = creaNodo("PARAMS");}
;

param_list:				expr	{$$ = creaNodo("PARAM_LIST"); $$ = llenaHijos($<node>$, 1, $<node>1);}
				|		expr COMMA param_list {aux1 = creaNodo($<cadena>2); $$ = llenaHijosIzq($<node>3, 1, $<node>1);}
				
;


expr2:          /* lambda */ {auxexpr2 = 0; $$ = NULL;}
                |       op expr	{$$ = llenaHijosIzq($<node>1, 1, $<node>2); auxexpr2 = 1;}
;
op:                     PLUS	{$$ = creaNodo($<cadena>1);}
                |       MINUS	{$$ = creaNodo($<cadena>1);}
                |       BY		{$$ = creaNodo($<cadena>1);}
                |       DIVIDE	{$$ = creaNodo($<cadena>1);}
                |       MOD		{$$ = creaNodo($<cadena>1);}
                |       EQ		{$$ = creaNodo($<cadena>1);}
                |       NEG		{$$ = creaNodo($<cadena>1);}
                |       MIN		{$$ = creaNodo($<cadena>1);}
                |       MINE	{$$ = creaNodo($<cadena>1);}
                |       GRT		{$$ = creaNodo($<cadena>1);}
                |       GRTE	{$$ = creaNodo($<cadena>1);}
                |       AND		{$$ = creaNodo($<cadena>1);}
                |       OR		{$$ = creaNodo($<cadena>1);}
;
unary:		/* lambda */	{$$ = NULL;}
		|	MINUS unary		{aux1 = creaNodo($<cadena>1); if($<node>2 == NULL) $$ = aux1; else $$ = llenaHijosIzq($<node>2, 1, aux1);}
		|	PLUS unary		{aux1 = creaNodo($<cadena>1); if($<node>2 == NULL) $$ = aux1; else $$ = llenaHijosIzq($<node>2, 1, aux1);}
		| 	NOT unary		{aux1 = creaNodo($<cadena>1); if($<node>2 == NULL) $$ = aux1; else $$ = llenaHijosIzq($<node>2, 1, aux1);}
;		
vdecl:		ID COLON T_INT {fprintf(yyout,"%s\tvar\t%s\n", $1,$3); $$ = creaNodo("VDECL"); aux1 = creaNodo($<cadena>1); aux3 = creaNodo($<cadena>3); llenaHijos($<node>$, 2, aux1, aux3);}		
		|	ID COLON T_REAL {fprintf(yyout,"%s\tvar\t%s\n", $1,$3); $$ = creaNodo("VDECL"); aux1 = creaNodo($<cadena>1); aux3 = creaNodo($<cadena>3); llenaHijos($<node>$, 2, aux1, aux3);}		
		|	ID COLON T_BOOLEAN {fprintf(yyout,"%s\tvar\t%s\n", $1,$3); $$ = creaNodo("VDECL"); aux1 = creaNodo($<cadena>1); aux3 = creaNodo($<cadena>3); llenaHijos($<node>$, 2, aux1, aux3);}		
		|	ID COLON T_STRING {fprintf(yyout,"%s\tvar\t%s\n", $1,$3); $$ = creaNodo("VDECL"); aux1 = creaNodo($<cadena>1); aux3 = creaNodo($<cadena>3); llenaHijos($<node>$, 2, aux1, aux3);}		
		|	ID COLON T_INT ASSIGN expr			{fprintf(yyout,"%s\tvar\t%s\n", $1,$3);  $$ = creaNodo("VDECL"); aux1 = creaNodo($<cadena>1); aux3 = creaNodo($<cadena>3); aux4 = creaNodo($<cadena>4); llenaHijos($<node>$, 4, aux1, aux3, aux4, $<node>5);}
		|	ID COLON T_REAL ASSIGN expr			{fprintf(yyout,"%s\tvar\t%s\n", $1,$3);  $$ = creaNodo("VDECL"); aux1 = creaNodo($<cadena>1); aux3 = creaNodo($<cadena>3); aux4 = creaNodo($<cadena>4); llenaHijos($<node>$, 4, aux1, aux3, aux4, $<node>5);}
		|	ID COLON T_BOOLEAN ASSIGN expr		{fprintf(yyout,"%s\tvar\t%s\n", $1,$3);  $$ = creaNodo("VDECL"); aux1 = creaNodo($<cadena>1); aux3 = creaNodo($<cadena>3); aux4 = creaNodo($<cadena>4); llenaHijos($<node>$, 4, aux1, aux3, aux4, $<node>5);}
		|	ID COLON T_STRING ASSIGN expr		{fprintf(yyout,"%s\tvar\t%s\n", $1,$3);  $$ = creaNodo("VDECL"); aux1 = creaNodo($<cadena>1); aux3 = creaNodo($<cadena>3); aux4 = creaNodo($<cadena>4); llenaHijos($<node>$, 4, aux1, aux3, aux4, $<node>5);}
		|	ID COLON T_INT {fprintf(yyout,"%s\tvar\t%s", $1,$3);} arrdecl2 adecl		{  $$ = creaNodo("VDECL"); aux1 = creaNodo($<cadena>1); aux3 = creaNodo($<cadena>3); llenaHijos($<node>$, 3, aux1, aux3, $<node>5); if (auxadecl == 1) llenaHijos($<node>$, 1, $<node>6); auxadecl = 0; }
		|	ID COLON T_REAL {fprintf(yyout,"%s\tvar\t%s", $1,$3);} arrdecl2 adecl		{  $$ = creaNodo("VDECL"); aux1 = creaNodo($<cadena>1); aux3 = creaNodo($<cadena>3); llenaHijos($<node>$, 3, aux1, aux3, $<node>5); if (auxadecl == 1) llenaHijos($<node>$, 1, $<node>6); auxadecl = 0; }
		|	ID COLON T_BOOLEAN {fprintf(yyout,"%s\tvar\t%s", $1,$3);} arrdecl2 adecl	{  $$ = creaNodo("VDECL"); aux1 = creaNodo($<cadena>1); aux3 = creaNodo($<cadena>3); llenaHijos($<node>$, 3, aux1, aux3, $<node>5); if (auxadecl == 1) llenaHijos($<node>$, 1, $<node>6); auxadecl = 0; }
		|	ID COLON T_STRING {fprintf(yyout,"%s\tvar\t%s", $1,$3);} arrdecl2 adecl		{  $$ = creaNodo("VDECL"); aux1 = creaNodo($<cadena>1); aux3 = creaNodo($<cadena>3); llenaHijos($<node>$, 3, aux1, aux3, $<node>5); if (auxadecl == 1) llenaHijos($<node>$, 1, $<node>6); auxadecl = 0; }
;

adecl:		/* lambda */ {$$ = creaNodo("ADECL");}
		|	ASSIGN arr_init {auxadecl = 1; $$ = creaNodo("ADECL"); aux1 = creaNodo($<cadena>1); llenaHijos($<node>$, 2, aux1, $<node>2);}

asig:		ID array ASSIGN expr {$$ = creaNodo("ASIG"); aux1 = creaNodo($<cadena>1); aux2 = creaNodo($<cadena>3); llenaHijos($<node>$, 1, aux1);if(auxarray) $$ = llenaHijos($<node>$, 1, $<node>2); $$ = llenaHijos($<node>$, 2, aux2, $<node>4); auxarray = 0;}
;
fdecl:		aux SEMICOLON {$$ = creaNodo("F_DECL"); llenaHijos($<node>$, 1,$<node>1);}											
		|	aux in_prm SEMICOLON	{$$ = creaNodo("F_DECL"); $$ = llenaHijos($<node>$, 2,$<node>1, $<node>2);}
		|	aux ARROW out_prm SEMICOLON		{$$ = creaNodo("F_DECL"); $$ = llenaHijos($<node>$, 2, $<node>1, $<node>3);}
		|	aux in_prm ARROW out_prm SEMICOLON	{$$ = creaNodo("F_DECL"); $$ = llenaHijos($<node>$, 3, $<node>1, $<node>2, $<node>4);}
		|	aux OPEN_BRACE body CLOSE_BRACE		{$$ = creaNodo("F_DECL"); $$ = llenaHijos($<node>$, 1, $<node>1); if (auxbody) $$ = llenaHijos($<node>$, 1, $<node>3); auxbody = 0; }
		|	aux in_prm  OPEN_BRACE body CLOSE_BRACE		{$$ = creaNodo("F_DECL"); $$ = llenaHijos($<node>$, 2, $<node>1, $<node>2); if (auxbody) llenaHijos($<node>$, 1, $<node>4); auxbody = 0; }
		|	aux ARROW out_prm  OPEN_BRACE body CLOSE_BRACE	{$$ = creaNodo("F_DECL"); $$ = llenaHijos($<node>$, 2, $<node>1, $<node>3); if (auxbody) $$ = llenaHijos($<node>$, 1, $<node>5); auxbody = 0; }
		|	aux in_prm ARROW out_prm  OPEN_BRACE body CLOSE_BRACE	{$$ = creaNodo("F_DECL"); $$ = llenaHijos($<node>$, 3, $<node>1, $<node>2, $<node>4); if (auxbody) $$ = llenaHijos($<node>$, 1, $<node>6); auxbody = 0; }
;
aux:	ID COLON FUNCTION {$$ = creaNodo("F_NAME"); aux1 = creaNodo($<cadena>1); $$ = llenaHijos($<node>$, 1,  aux1); fprintf(yyout,"%s\t%s\n", $1,$3);}
;

in_prm:		auxt	{$$ = creaNodo("IN_PRM"); $$ = llenaHijos($<node>$, 1, $<node>1);}						
		|	auxt COMMA in_prm	{$$ = llenaHijos($<node>3, 1, $<node>1);}
;

auxt:	ID COLON T_INT						{fprintf(yyout,"%s\tvar\t%s\n", $1,$3); $$ = creaNodo("NAME&TYPE"); aux1 = creaNodo($<cadena>1); aux3 = creaNodo($<cadena>3); $$ = llenaHijos($<node>$, 2, aux1, aux3);}
		|	ID COLON T_REAL					{fprintf(yyout,"%s\tvar\t%s\n", $1,$3); $$ = creaNodo("NAME&TYPE"); aux1 = creaNodo($<cadena>1); aux3 = creaNodo($<cadena>3); $$ = llenaHijos($<node>$, 2, aux1, aux3);}
		|	ID COLON T_BOOLEAN				{fprintf(yyout,"%s\tvar\t%s\n", $1,$3); $$ = creaNodo("NAME&TYPE"); aux1 = creaNodo($<cadena>1); aux3 = creaNodo($<cadena>3); $$ = llenaHijos($<node>$, 2, aux1, aux3);}
		|	ID COLON T_STRING				{fprintf(yyout,"%s\tvar\t%s\n", $1,$3); $$ = creaNodo("NAME&TYPE"); aux1 = creaNodo($<cadena>1); aux3 = creaNodo($<cadena>3); $$ = llenaHijos($<node>$, 2, aux1, aux3);}
		
		
out_prm:	auxt	{$$ = creaNodo("OUT_PRM"); $$ = llenaHijos($<node>$, 1, $<node>1);}
		|	auxt COMMA out_prm 	{$$ = llenaHijos($<node>3, 1, $<node>1);}

;
body:           /* lambda */	{ $$ = creaNodo("BODY");}
                |       vdecl SEMICOLON body	{auxbody = 1; $$ = llenaHijosIzq($<node>3, 1, $<node>1);}
                |       asig SEMICOLON body		{auxbody = 1; $$ = llenaHijosIzq($<node>3, 1, $<node>1);}
                |      	ID body_params SEMICOLON body		{auxbody = 1; aux1 = creaNodo($<cadena>1); $$ = llenaHijos(aux1, 1, $<node>2); $$ = llenaHijosIzq($<node>4, 1, $<node>$);}					
                |       IF expr THEN  OPEN_BRACE body CLOSE_BRACE elseif else body	{auxbody = 1; aux3 = creaNodo("IF"); aux1 = creaNodo("CONDITION"); $$ = llenaHijos(aux1, 1, $<node>2); $$ = llenaHijos(aux3, 2, $<node>$, $<node>5); if (auxelseif == 1) llenaHijos($<node>$, 1, $<node>7); if (auxelse == 1) llenaHijos($<node>$, 1, $<node>8); $$ = llenaHijosIzq($<node>9, 1, $<node>$); auxelseif = 0; auxelse = 0; auxbody = 1; }					
                |       WHILE OPEN_PAREN expr CLOSE_PAREN LOOP OPEN_BRACE body CLOSE_BRACE ifnever ifquit body	{aux2 = creaNodo("WHILE");aux1 = creaNodo("CONDITION"); $$ = llenaHijos(aux1, 1, $<node>3); $$ = llenaHijos(aux2, 1, $<node>$); $$ = llenaHijos($<node>$, 1, $<node>7); if(auxifnever) $$ = llenaHijos($<node>$, 1, $<node>9); if(auxifquit) $$ = llenaHijos($<node>$, 1, $<node>10); $$ = llenaHijosIzq($<node>11, 1, $<node>$); auxbody = 1; auxifnever = 0; auxifquit = 0;}
;
elseif:         	    ELSEIF expr OPEN_BRACE body CLOSE_BRACE  elseif		{auxelseif = 1; aux1 = creaNodo("ELSE_IF"); aux2 = creaNodo("CONDITION"); $$ = llenaHijos(aux2, 1, $<node>2); $$ = llenaHijos(aux1, 1, $<node>$); $$ = llenaHijos($<node>$, 1, $<node>4); $$ = llenaHijosIzq($<node>6, 1, aux1);}
                 |       /* lambda */ {$$ = creaNodo("ELSEIF");}
;
else:		/* lambda */	{$$ = creaNodo("ELSE");}
		|	ELSE  OPEN_BRACE body CLOSE_BRACE 	{auxelse = 1; $$ = creaNodo("ELSE"); $$ = llenaHijos($<node>$, 1, $<node>3);}
;
ifnever:	IFNEVER OPEN_BRACE body CLOSE_BRACE {auxifnever = 1; $$ = creaNodo("IFNEVER"); $$ = llenaHijos($<node>$, 1, $<node>3);}
		|	/* lambda */ 	{$$ = creaNodo("IFNEVER");}
;
ifquit:		/* lambda */	{$$ = creaNodo("IFQUIT");}
		|	IFQUIT OPEN_BRACE body CLOSE_BRACE	{auxifquit = 1; $$ = creaNodo("IFQUIT"); $$ = llenaHijos($<node>$, 1, $<node>3);}
;

array:		/* lambda */	{ $$ = creaNodo("ARRAY");}
		 |	OPEN_BRACKET expr CLOSE_BRACKET array	{auxarray=1; aux1 = creaNodo($<cadena>1); aux2 = creaNodo($<cadena>3); $$ = llenaHijos($<node>4, 3, aux1, $<node>2, aux2);}
;

arrdecl:	/* lambda */								{fprintf(yyout,"\n"); $$ = creaNodo("ARR_DECL");}
		 |	OPEN_BRACKET expr_arr CLOSE_BRACKET  {auxarrdecl = 1; fprintf(yyout,"[]");} arrdecl	{aux1 = creaNodo($<cadena>1); aux2 = creaNodo($<cadena>3); $$ = llenaHijos($<node>5, 3, aux1, $<node>2, aux2);}
;

arrdecl2:	OPEN_BRACKET expr_arr CLOSE_BRACKET  {fprintf(yyout,"[]");} arrdecl {$$ = creaNodo("ARRAY_DECL"); aux1 = creaNodo($<cadena>1); aux2 = creaNodo($<cadena>3); $$ = llenaHijos($<node>$, 4, aux1, $<node>2, aux2, $<node>5);}
;

arr_init:	OPEN_BRACE arr_exp CLOSE_BRACE		{$$ = creaNodo("ARR_INIT"); aux1 = creaNodo($<cadena>1); aux2 = creaNodo($<cadena>3); $$ = llenaHijos($<node>$, 3, aux1, $<node>2, aux2);}
;

arr_exp:		expr	{$$ = creaNodo("ARR_EXP"); $$ = llenaHijos($<node>$, 1, $<node>1);}
			|	expr COMMA arr_exp	{$$ = llenaHijos($<node>3, 1, $<node>1);}
			|	OPEN_BRACE arr_exp CLOSE_BRACE	{$$ = creaNodo("ARR_EXP"); $$ = llenaHijos($<node>$, 1, $<node>2);}
			|	OPEN_BRACE arr_exp CLOSE_BRACE COMMA arr_exp	{$$ = llenaHijos($<node>5, 1, $<node>2);}

expr_arr:	/* lambda */	{$$ = creaNodo("EXPR_ARR");}
		 |	expr			{$$ = creaNodo("EXPR_ARR"); $$ = llenaHijos($<node>$, 1, $<node>1);}
%%



void
yyerror (char const *s)
{
  fprintf (stderr, "%s\n", s);
}

int
main (int argc, char * argv[])
{
	char * output_name;
	char * output_tree;
	int ret;
	if ( argc != 2 ){
		fprintf(stdout, "Faltan parametros\nUSO: prueba nombre_de_archivo\n");
		return 1;
	}
	if((input = fopen(argv[1],"r")) == NULL){
		fprintf(stdout, "No se pudo abrir el archivo %s\n", argv[1]);
		return 1;
	}
	
	if((output_name = calloc(sizeof(char), strlen(argv[1]) + strlen(".st"))) == NULL){
		return 1;
	}
	strcpy(output_name, argv[1]);
	strcat(output_name, ".st");
	if((fd = fopen(output_name,"w")) == NULL){
		fprintf(stdout, "No se pudo crear el archivo de salida\n");
		fclose(input);
		return 1;
	}
	fprintf(stdout, "\nInicializando el analizador lexico y sintactico...\n\nAnalizando : %s\n", argv[1]);
	yyin = input;
	yyout =fd;
	ret = yyparse ();
	if( ret == 1 ){
		fprintf(stdout, "\nHa ocurrido un error analizando el codigo... Liberando Recursos\n");
		fclose(fd);
		fclose(input);
		free(output_name);
		remove(output_name);
		return 1;
	}else{
		fclose(fd);
		fclose(input);
		//PrintTree(root, 0);
		if((output_tree = calloc(sizeof(char), strlen(argv[1]) + strlen(".dot"))) == NULL){
			return 1;
		}
		strcpy(output_tree, argv[1]);
		strcat(output_tree, ".dot");
		BuildGraphViz(root, output_tree);
	}
	fprintf(stdout, "\n¡El analisis se ha finalizado exitosamente!\n\nPuede encontrar los resultados de\nLa Tabla de Simbolos en: %s\nEl Arbol Sintactico en: %s\n", output_name, output_tree);
	fprintf(stdout, "\nTrabajo Practico Teoria de Lenguajes y Automatas\n\tAberg Cobo, Simon\n\tOybin, Nahuel\n\tTenaillon, Juan\n");
	return 0;
}

